/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pb138.jprvyd;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.exist.xmldb.EXistResource;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.*;
import org.xmldb.api.modules.XPathQueryService;

/**
 *
 * @author xholusa
 */
public class PaymentManagerImpl implements PaymentManager {

    private String URI;
    private Database database = new org.exist.xmldb.DatabaseImpl();
    private Collection col;
    private XPathQueryService xpqs;
    private FileHandler handler;
    public static final Logger l = Logger.getLogger("InvoiceManagerImplLogger");

    public PaymentManagerImpl() {
        this("xmldb:exist://localhost:8080/exist/xmlrpc/db/jprvyd");
    }

    public PaymentManagerImpl(String u) {
        try {
            this.URI = u;
            handler = new FileHandler("log/log.log");
            l.addHandler(handler);

            DatabaseManager.registerDatabase(database);
            col = DatabaseManager.getCollection(URI);
            xpqs = (XPathQueryService) col.getService("XPathQueryService", "1.0");
            xpqs.setProperty("indent", "yes");
        } catch (Exception ex) {
            l.log(Level.SEVERE, "Error establishing database connection", ex);
        }
        l.log(Level.INFO, "DB connection established");
    }
    
    @Override
    public void createPayment(Payment payment) throws IllegalEntityException {
        l.log(Level.INFO, "PaymentManagerImpl.createPayment: Creating Payment");

        if (payment == null) {
            String info = "PaymentManagerImpl.createPayment: Payment is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }
        if (payment.getId() != null) {
            String info = "PaymentManagerImpl.createPayment: id is set";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }    
        
        if (payment.getBankAccount() == null) {
            String info = "PaymentManagerImpl.createPayment: bank account is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }        

        if (payment.getDate() == null) {
            String info = "PaymentManagerImpl.createPayments: date is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }    

        try {
            Long id;
            if (this.getAllPayments().isEmpty()) {
                id = Long.parseLong("1");
            } else {
                id = Long.parseLong(xpqs.query("max(for $x in doc(\"payments.xml\")//payment return $x/@id)").getIterator().nextResource().getContent().toString());
                id++;
            }
            payment.setId(id);
            String node = getNode(payment);

            String query = "update insert " + node + " into doc(\"payments.xml\")//payments";

            ResourceSet res = xpqs.query(query);

        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentManagerImpl.createPayment: Exception creating Payment: ", ex);
        }

        l.log(Level.INFO, "PaymentManagerImpl.createPayment: Payment created");
    }

    @Override
    public void removePayment(Long id) {
        l.log(Level.INFO, "PaymentManagerImpl.removePayment: Removing Payment");

        if (id == null) {
            String info = "PaymentManagerImpl.removePayment: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        try {
            String query = "for $payment in doc(\"payments.xml\")//payment[@id=\"" + id + "\"] return update delete $payment";
            xpqs.query(query);

        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentManagerImpl.removePayment: Exception removing Payment: ", ex);
        }

        l.log(Level.INFO, "PaymentManagerImpl.removePayment: Payment removed");
    }

    @Override
    public void updatePayment(Payment payment) throws IllegalEntityException {
        if (payment == null) {
            String info = "PaymentManagerImpl.updatePayment: Payment is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }
        
        if (payment.getId() == null) {
            String info = "PaymentManagerImpl.updatePayments: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }    
        
        if (payment.getBankAccount() == null) {
            String info = "PaymentManagerImpl.updatePayment: bank account is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        if (payment.getDate() == null) {
            String info = "PaymentManagerImpl.updatePayments: date is null";
            l.log(Level.SEVERE, info);
            throw new IllegalEntityException(info);
        }

        try {
            String node = getNode(payment);

            if (xpqs.query("for $x in doc(\"payments.xml\")//payment[@id=\"" + payment.getId() + "\"] return $x/variablesymbol").getIterator().hasMoreResources() == false) {
                throw new IllegalEntityException("PaymentManagerImpl.updatePayment: Payment does not exist");
            }


            String query = "update replace doc(\"payments.xml\")//payment[@id=" + payment.getId() + "] with " + node;
            xpqs.query(query);
        } catch (XMLDBException ex) {
            l.log(Level.SEVERE, "PaymentManagerImpl.updatePayment: Exception updating Payment ", ex);
        }
        l.log(Level.INFO, "PaymentManagerImpl.updatePayment: Payment updated");
    }

    @Override
    public Payment getPaymentById(Long id) {
        l.log(Level.INFO, "PaymentManagerImpl.getPaymentById: Retrieving Payment");

        if (id == null) {
            String info = "PaymentManagerImpl.getPaymentById: id is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        String where = "@id=" + Long.toString(id);

        return getPaymentForm(where);
    }

    @Override
    public Payment getPaymentByVariableSymbol(int variableSymbol) {
        l.log(Level.INFO, "PaymentManagerImpl.getPaymentByVariableSymbol: Retrieving Payment");

        Integer i = variableSymbol;
        if (i == null) {
            String info = "PaymentManagerImpl.getPaymentByIco: variable symbol is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        return getPaymentForm("variablesymbol = " + Integer.toString(variableSymbol));
    }

    @Override
    public List<Payment> getPaymentsByDate(Date date) {
        if (date == null) {
            String info = "PaymentManagerImpl.getPaymentByDate: date is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }

        DateFormat dateF = new SimpleDateFormat("yyyy-MM-dd");
        String where = "where date=\"" + dateF.format(date) + "\"";

        return getPaymentsForm(where);
    }

    @Override
    public List<Payment> getPaymentsByBankAccount(String bankAccount) {
        if (bankAccount == null) {
            String info = "PaymentManagerImpl.getPaymentByBankAccount: bank account is null";
            l.log(Level.SEVERE, info);
            throw new IllegalArgumentException(info);
        }
        
        String where = "where bankaccount=\"" + bankAccount + "\"";

        return getPaymentsForm(where);
    }

    @Override
    public List<Payment> getAllPayments() {
        return getPaymentsForm("");
    }
    
    private String getNode(Payment payment) {
        DateFormat dateF = new SimpleDateFormat("yyyy-MM-dd");
        
        String node = "<payment id=\"" + payment.getId() + "\">"
                + "<date>" + dateF.format(payment.getDate()) + "</date>"
                + "<variablesymbol>" + payment.getVariableSymbol() + "</variablesymbol>"
                + "<bankaccount>" + payment.getBankAccount() + "</bankaccount>"
                + "<money>" + payment.getMoney() + "</money>"
                + "</payment>";                

        return node;
    }
    
    private Payment getPaymentForm(String where) {
        Payment payment = new Payment();     

        try {
            String queryStart = "for $x in doc(\"payments.xml\")//payment where ";
            String queryEnd = " return data($x/";

            ResourceSet resultId = xpqs.query(queryStart + where + queryEnd + "@id)");            
            ResourceSet resultDateString = xpqs.query(queryStart + where + queryEnd + "date)");            
            ResourceSet resultVariableSymbol = xpqs.query(queryStart + where + queryEnd + "variablesymbol)");
            ResourceSet resultMoney = xpqs.query(queryStart + where + queryEnd + "money)");
            ResourceSet resultBankAccount = xpqs.query(queryStart + where + queryEnd + "bankaccount)");
            

            ResourceIterator iterId = resultId.getIterator();            
            ResourceIterator iterDateString = resultDateString.getIterator();            
            ResourceIterator iterVariableSymbol = resultVariableSymbol.getIterator();
            ResourceIterator iterMoney = resultMoney.getIterator();
            ResourceIterator iterBankAccount = resultBankAccount.getIterator();
            

            Resource resId = null;            
            Resource resDateString = null;
            Resource resVariableSymbol = null;
            Resource resMoney = null;
            Resource resBankAccount = null;

            try {
                resId = iterId.nextResource();                
                resDateString = iterDateString.nextResource();
                resVariableSymbol = iterVariableSymbol.nextResource();
                resMoney = iterMoney.nextResource();
                resBankAccount = iterBankAccount.nextResource();

                DateFormat dateF = new SimpleDateFormat("yyyy-MM-dd");

                payment.setId(Long.parseLong(resId.getContent().toString()));
                payment.setDate(dateF.parse(resDateString.getContent().toString()));                
                payment.setVariableSymbol(Integer.parseInt(resVariableSymbol.getContent().toString()));
                payment.setMoney(Integer.parseInt(resMoney.getContent().toString()));
                payment.setBankAccount(resBankAccount.getContent().toString());
                
            } catch (Exception ex) {
                l.log(Level.SEVERE, "PaymentManagerImpl.getPaymentForm: Exception getting Payment: ", ex);
                throw new Exception(ex);
            } finally {
                try {
                    ((EXistResource) resId).freeResources();                    
                    ((EXistResource) resDateString).freeResources();
                    ((EXistResource) resVariableSymbol).freeResources();
                    ((EXistResource) resMoney).freeResources();
                    ((EXistResource) resBankAccount).freeResources();
                } catch (Exception ex) {
                    l.log(Level.SEVERE, "PaymentManagerImpl.getPaymentForm: Exception freeing up resources: ", ex);
                }
            }

        } catch (Exception ex) {
            l.log(Level.SEVERE, "PaymentManagerImpl.getPaymentForm: Excepion occured: ", ex);
        } finally {
            if (col != null) {
                try {
                    col.close();
                } catch (XMLDBException ex) {
                    l.log(Level.SEVERE, "PaymentManagerImpl.getPaymentForm: Exception closing collection: ", ex);
                }
            }
        }
        l.log(Level.INFO, "PaymentManagerImpl.getPaymentForm: Payment retrieved");
        if (payment.getId() == null) {
            l.log(Level.INFO, "PaymentManagerImpl.getPaymentForm: No Payment found");
            return null;
        } else {
            l.log(Level.INFO, "PaymentManagerImpl.getPaymentForm: Payment retrieved");
            return payment;
        }
    }
    
    private List<Payment> getPaymentsForm(String where) throws NumberFormatException {
        List<Payment> payments = new ArrayList<Payment>();

        try {
            ResourceSet result = xpqs.query("for $x in doc(\"payments.xml\")//payment "
                    + where + " return data($x/@id)");

            ResourceIterator iter = result.getIterator();
            Resource res = null;
            while (iter.hasMoreResources()) {
                res = iter.nextResource();
                Long id = Long.parseLong(res.getContent().toString());
                payments.add(getPaymentById(id));
            }

        } catch (XMLDBException ex) {
            l.log(Level.SEVERE, null, ex);
        }

        return payments;
    }
    
    @Override
    public int getSumTotal() {                          
        l.log(Level.INFO, "PaymentManagerImpl.getSumTotal");
        int sum = 0;
        try {
            sum = Integer.parseInt(xpqs.query("sum(for $x in doc(\"payments.xml\")//payment return data($x/money))").getIterator().nextResource().getContent().toString());
        } catch (XMLDBException ex) {
            l.log(Level.SEVERE, "PaymentManagerImpl.getSumTotal: could not retrieve total", ex);
        }
        return sum;
        
    }
    
}
